package p.ithorns.sample.influxdb.core;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * BeanUtil
 * TODO
 *
 * @author HtL
 * @date 2025/1/17 10:51
 * @since 1.0.0
 */
public class BeanUtil {

    private final static Map<Class<?>, Map<String, Field>> CLASS_FIELDS_MAP = new ConcurrentHashMap<>();
    private final static Map<Class<?>, Map<String, Method>> CLASS_METHODS_MAP = new ConcurrentHashMap<>();
    private final static Map<Class<?>, Constructor<?>> CLASS_CONSTRUCTOR_MAP = new ConcurrentHashMap<>();

    public static Map<String, Field> getClassFields(Class<?> clazz) {
        return CLASS_FIELDS_MAP.computeIfAbsent(clazz,
                aClass -> Arrays.stream(aClass.getDeclaredFields())
                        .peek(field -> {
                            if (!field.isAccessible()) {
                                field.setAccessible(true);
                            }
                        })
                        .collect(Collectors.toMap(Field::getName, Function.identity(), (v1, v2) -> v1))
        );
    }

    public static Field getClassField(Class<?> clazz, String fieldName) {
        return Optional.ofNullable(getClassFields(clazz))
                .map(map -> map.get(fieldName)).orElse(null);
    }

    public static Map<String, Method> getClassMethods(Class<?> clazz) {
        return CLASS_METHODS_MAP.computeIfAbsent(clazz,
                aClass -> Arrays.stream(aClass.getDeclaredMethods())
                        .peek(method -> {
                            if (!method.isAccessible()) {
                                method.setAccessible(true);
                            }
                        })
                        .collect(Collectors.toMap(Method::getName, Function.identity(), (v1, v2) -> v1))
        );
    }

    public static Method getClassMethod(Class<?> clazz, String methodName) {
        return Optional.ofNullable(getClassMethods(clazz))
                .map(map -> map.get(methodName)).orElse(null);
    }

    public static <T> T instance(Class<T> clazz) {
        try {
            Constructor<?> constructor = CLASS_CONSTRUCTOR_MAP.get(clazz);
            if (null == constructor) {
                constructor = clazz.getDeclaredConstructor();
                CLASS_CONSTRUCTOR_MAP.put(clazz, constructor);
            }
            return (T) constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}